/*
 * Copyright (c) 2025 DuckDuckGo
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.duckduckgo.app.browser.httperrors

import com.duckduckgo.app.browser.BrowserTabViewModel
import com.duckduckgo.app.global.model.Site
import javax.inject.Inject

interface SiteErrorHandler<T> {
    fun handleError(currentSite: Site?, urlWithError: String, error: T)
    fun assignErrorsAndClearCache(site: Site?)
}

interface StringSiteErrorHandler : SiteErrorHandler<String>

interface HttpCodeSiteErrorHandler : SiteErrorHandler<Int>

/**
 * When navigating from one page to another it can happen that errors are received before [BrowserTabViewModel.pageChanged].
 * So, a previous page is still loaded, but we are already receiving potential errors about a page that's about to be loaded.
 * For site breakage reports, we want to include these errors as well.
 *
 * This class provides a [handleError] function that takes the current loaded site and the received error as parameters.
 * If the error is generated by the current site, it's immediately assigned.
 * If not, a temporary cache is used to hold onto errors and later assign them to a newly loaded [Site].
 *
 * [assignErrorsAndClearCache] should be called as soon as a new [Site] is built.
 * The function will assign previously captured errors that match the [Site.url] and drop everything else.
 *
 * This means that if we encounter some intermediate errors on redirects that are not part of the target site, they won't be included in the report.
 */
sealed class SiteErrorHandlerImpl<T> : SiteErrorHandler<T> {
    private val cache = mutableMapOf<String, MutableList<T>>()

    override fun handleError(currentSite: Site?, urlWithError: String, error: T) {
        if (currentSite != null && currentSite.url == urlWithError) {
            assignError(currentSite, error)
        } else {
            val cachedErrorsForSite = cache.getOrPut(urlWithError) {
                mutableListOf()
            }
            cachedErrorsForSite.add(error)
        }
    }

    override fun assignErrorsAndClearCache(site: Site?) {
        if (site != null) {
            cache[site.url]?.forEach {
                assignError(site, it)
            }
        }
        cache.clear()
    }

    protected abstract fun assignError(site: Site, error: T)
}

class StringSiteErrorHandlerImpl @Inject constructor() : StringSiteErrorHandler, SiteErrorHandlerImpl<String>() {
    override fun assignError(site: Site, error: String) {
        site.onErrorDetected(error)
    }
}

class HttpCodeSiteErrorHandlerImpl @Inject constructor() : HttpCodeSiteErrorHandler, SiteErrorHandlerImpl<Int>() {
    override fun assignError(site: Site, error: Int) {
        site.onHttpErrorDetected(error)
    }
}
